package com.vhall.component.service.praise.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.obs.services.IFSClient;
import com.vhall.component.dao.lottery.LotteryJoinsMapper;
import com.vhall.component.dao.praise.RoomLikeMapper;
import com.vhall.component.dao.room.RoomForwardMapper;
import com.vhall.component.entity.inav.SendMsgPassBodyDTO;
import com.vhall.component.entity.lottery.entity.LotteryJoinsEntity;
import com.vhall.component.entity.praise.vo.RoomLikeReqVO;
import com.vhall.component.entity.room.RoomLikesEntity;
import com.vhall.component.entity.room.dto.RoomJoinsEntityDTO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.room.entity.RoomForwardEntity;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.VssToken;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.common.utils.RedisLocker;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.praise.RoomLikeService;
import com.vhall.component.service.room.RoomInnerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * @author lsl
 * @since 2021/6/15
 */
@Slf4j
@Service
public class RoomLikeServiceImpl implements RoomLikeService {

    @Autowired
    private RoomInnerService roomInnerService;

    @Autowired
    private RoomLikeMapper roomLikeMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private PaasService paasService;

    @Resource
    private RoomForwardMapper roomForwardMapper;

    @Autowired
    private LotteryJoinsMapper lotteryJoinsMapper;

    @Override
    public Long countIsLike(String accountId, String roomId) {
        return roomLikeMapper.countIsLike(accountId, roomId);
    }


    @Override
    public void like(RoomLikeReqVO reqVO, VssToken token) {
        RoomsEntityDTO rooms = roomInnerService.getRoomInfo(reqVO.getRoomId());
        if (rooms == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }

        String lockValue = "1";
        String msgCacheKey = MessageFormat.format("{0}-{1}", reqVO.getRoomId(), token.getThirdPartyUserId());
        if (!RedisLocker.tryLock(msgCacheKey, lockValue, 3 * 1000L)) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                log.error("Interrupted!", e);
                Thread.currentThread().interrupt();
            }
            log.error("like 没有拿到锁，等了一下，继续访问");
            like(reqVO, token);
            return;
        }
        try {
            String key = RedisKey.ROOM_LIKE_ACCOUNT + rooms.getRoomId();
            redisTemplate.opsForSet().add(key, token.getThirdPartyUserId());
            redisTemplate.expire(key, 1, TimeUnit.DAYS);
            dealLike(reqVO, token, rooms);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(MessageFormat.format("like 异常：【{0}】", e.getMessage()));
        } finally {
            RedisLocker.unlock(msgCacheKey, lockValue);
        }

    }

    private void dealLike(RoomLikeReqVO reqVO, VssToken token, RoomsEntityDTO rooms) {
        String key = RedisKey.ROOM_LIKE_COUNTS + rooms.getRoomId();
        if (!reqVO.getType().equals(0)) {
            recordAccountLike(reqVO, token, key);
        } else {
            redisTemplate.opsForValue().increment(key, reqVO.getNum());
        }
        if (!waitWrite(RedisKey.ROOM_LIKE_WAIT_CACHE + rooms.getRoomId(), 300)) {
            Object count = redisTemplate.opsForValue().get(key);
            int like = rooms.getLike() + (int) count;
            redisTemplate.opsForValue().decrement(key, (int) count);
            updateLike(like, rooms);
            SendMsgPassBodyDTO passBodyDTO = new SendMsgPassBodyDTO();
            passBodyDTO.setType("room_like_num");
            passBodyDTO.setLike(like);
            String body = JsonUtil.toJsonString(passBodyDTO);
            paasService.sendMessage(rooms.getChannelId(), body, null, null, null, null);
        }
    }

    /**
     * 记录点赞用户
     * @param reqVO
     * @param token
     * @param key
     */
    private void recordAccountLike(RoomLikeReqVO reqVO, VssToken token, String key) {
        RoomJoinsEntityDTO currentJoinUser = roomInnerService.getRoomJoinInfo(token.getThirdPartyUserId(), reqVO.getRoomId());
        LambdaQueryWrapper<RoomLikesEntity> where = Wrappers.lambdaQuery();
        where.eq(RoomLikesEntity::getRoomId, reqVO.getRoomId())
                .eq(RoomLikesEntity::getAccountId, currentJoinUser.getAccountId());
        RoomLikesEntity like = roomLikeMapper.selectOne(where);
        if (like != null) {
            like.setLikeCount(like.getLikeCount() + reqVO.getNum());
            roomLikeMapper.updateById(like);
        } else {
            redisTemplate.opsForValue().increment(key);
            RoomLikesEntity entity = new RoomLikesEntity();
            entity.setRoomId(reqVO.getRoomId());
            entity.setAccountId(currentJoinUser.getAccountId());
            entity.setLikeCount(Long.valueOf(reqVO.getNum()));
            entity.setCreatedAt(LocalDateTime.now());
            entity.setUpdatedAt(LocalDateTime.now());
            roomLikeMapper.insert(entity);
        }
        redisTemplate.opsForValue().increment(key, reqVO.getNum());
    }

    @Override
    public void forward(RoomLikeReqVO reqVO, VssToken token) {
        RoomForwardEntity roomForwardEntity = new RoomForwardEntity();
        roomForwardEntity.setRoomId(reqVO.getRoomId());
        roomForwardEntity.setAccountId(token.getThirdPartyUserId());
        roomForwardMapper.insert(roomForwardEntity);
    }

    private void updateLike(int like, RoomsEntityDTO rooms) {
        rooms.setLike(like);
        rooms.setUpdatedAt(LocalDateTime.now());
        roomInnerService.updateLikeById(rooms);
    }

    private boolean waitWrite(String key, long mesc) {
        Long count = redisTemplate.opsForValue().increment(key);
        if (count <= 1 || redisTemplate.opsForValue().getOperations().getExpire(key) < 0) {
            redisTemplate.expire(key, mesc, TimeUnit.MILLISECONDS);
        }
        return count > 1;
    }


}
